package com.witmore.skyline.web.task;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.witmore.skyline.db.entity.DeviceDO;
import com.witmore.skyline.db.mapper.DeviceMapper;
import com.witmore.skyline.web.service.impl.DeviceOilServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.util.List;

/**
 * Description: 油位数据缓存预热定时任务
 * 功能：每天定时拉取昨天的油位数据并缓存到Redis
 * 目的：避免用户查询时因为限流导致数据不完整
 * Author: 豆子高
 * Date: 2025/11/17
 */
@Profile({"prod"})  // 只在生产环境运行
@Slf4j
@Service
public class DeviceOilCacheWarmUpTask {

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceOilServiceImpl deviceOilService;

    /**
     * 每天早上8点和晚上20点执行
     */
    @Scheduled(cron = "0 0 8,20 * * ?")
    public void executeWarmUpCache() {
        log.info("=== 开始执行油位缓存预热任务 ===");
        warmUpYesterdayCache();
    }

    /**
     * 预热昨天的油位数据缓存
     */
    private void warmUpYesterdayCache() {
        long startTime = System.currentTimeMillis();

        try {
            // 1. 获取昨天的日期
            LocalDate yesterday = LocalDate.now().minusDays(1);
            log.info("开始预热日期：{} 的油位数据", yesterday);

            // 2. 查询所有有效的设备（未删除的设备）
            LambdaQueryWrapper<DeviceDO> queryWrapper = new LambdaQueryWrapper<DeviceDO>()
                    .eq(DeviceDO::getIsDeleted, 0)
                    .isNotNull(DeviceDO::getDeviceSn)
                    .orderByAsc(DeviceDO::getId);

            List<DeviceDO> deviceList = deviceMapper.selectList(queryWrapper);

            if (CollectionUtils.isEmpty(deviceList)) {
                log.warn("没有找到需要预热缓存的设备");
                return;
            }

            log.info("共找到 {} 个设备需要预热油位缓存", deviceList.size());

            // 3. 遍历设备，拉取昨天的油位数据
            int successCount = 0;
            int failCount = 0;
            int skipCount = 0;

            for (int i = 0; i < deviceList.size(); i++) {
                DeviceDO device = deviceList.get(i);
                String deviceSn = device.getDeviceSn();

                try {
                    log.info("正在处理设备 [{}/{}]: {}", i + 1, deviceList.size(), deviceSn);

                    // 调用 Service 方法获取昨天的数据（会自动缓存）
                    // 这个方法会检查缓存，如果已有缓存则直接返回，没有则拉取并缓存
                    var dayData = deviceOilService.getHistoryDayDataFromCache(deviceSn, yesterday);

                    if (dayData == null) {
                        log.warn("设备 {} 昨天油位数据获取失败（接口异常/限流），跳过", deviceSn);
                        failCount++;
                    } else if (dayData.isEmpty()) {
                        log.info("设备 {} 昨天油位数据为空（正常情况）", deviceSn);
                        successCount++;
                    } else {
                        log.info("设备 {} 昨天油位数据预热成功，数据量: {}", deviceSn, dayData.size());
                        successCount++;
                    }

                    // 为了避免触发限流，设备之间添加延迟（重要！）
                    // 云机械限流规则：同一接口、同一设备 两分钟内调用次数有限
                    // 不同设备之间的调用不受此限制，但仍需适当延迟避免总体限流
                    if (i < deviceList.size() - 1) {
                        Thread.sleep(100);
                    }

                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error("定时任务被中断，已处理 {} 个设备", i);
                    break;
                } catch (Exception e) {
                    log.error("设备 {} 预热缓存时发生异常: {}", deviceSn, e.getMessage(), e);
                    failCount++;
                }
            }

            long endTime = System.currentTimeMillis();
            long duration = (endTime - startTime) / 1000;

            log.info("=== 油位缓存预热任务完成 ===");
            log.info("执行时间: {} 秒", duration);
            log.info("设备总数: {}, 成功: {}, 失败: {}, 跳过: {}",
                    deviceList.size(), successCount, failCount, skipCount);

        } catch (Exception e) {
            log.error("油位缓存预热任务执行失败: {}", e.getMessage(), e);
        }
    }
}
